/**
 * Copyright (C) 2015-2018 IQIYI
 * All rights reserved.
 *
 * Author     : 王海斌
 * E-mail     : wanghaibin@qiyi.com
 * Version    :
 * Date       : Wed 08 Jul 2020 11:26:28 AM CST
 * Destription:
 */

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>


// 将ip port 转为 二进制连续数据
static bool format_ip(bool bv4, const std::string & ip, unsigned short port,
        char * data, int data_len)
{
    int domain = bv4?AF_INET:AF_INET6;
    if(inet_pton(domain, ip.c_str(), data)<=0) {
        return false;
    }
    size_t ip_len = data_len-(sizeof(unsigned short));
    memcpy(data+ip_len, (const char*)&port, sizeof(unsigned short));
    return true;
}

/// 从二进制数据中获取对应的ip和端口
static bool from_binary_ip_to_string(bool bv4, const char * data, int datalen,
        std::string & ip, unsigned short & port) {
    int domain = bv4?AF_INET:AF_INET6;
    int len = datalen-sizeof(unsigned short);
    char str[INET6_ADDRSTRLEN]={0};
    if(NULL==inet_ntop(domain, data, str, INET6_ADDRSTRLEN)) {
        return false;
    }
    memcpy((char*)&port, (const char*)(data+len), sizeof(unsigned short));
    ip = str;
    return true;
}

// v4 是否是ipv4
// ip 对应的ip
// port 端口
void test( bool v4, const std::string & ip, unsigned short port)
{
    static size_t V4_LEN  = sizeof(struct in_addr) + sizeof(unsigned short);
    static size_t V6_LEN  = sizeof(struct in6_addr) + sizeof(unsigned short);
    printf("v4_len=%zd, v6_len=%zd\n", V4_LEN, V6_LEN);
    size_t len = V4_LEN;
    if(!v4) {
        len = V6_LEN;
    }

    char str[INET6_ADDRSTRLEN+2];

    if(format_ip(v4, ip, port, str, len)) {
        printf("change %s:%u to binary ok, INET6_ADDRSTRLEN=%d\n", ip.c_str(), port, INET6_ADDRSTRLEN);
        std::string ip2;
        unsigned short port2 = 0;
        if(from_binary_ip_to_string(v4, str, len, ip2, port2)) {
            printf("from binary to str ok, ip=%s, port=%u\n", ip2.c_str(), port2);
        } else {
            printf("from binary to str failed.\n");
        }
    } else {
        printf("change %s:%u to binary failed.\n", ip.c_str(), port);
    }
}

int main(int argc, char *argv[])
{
    std::string ip("ABCD:EF01:2345:6789:ABCD:EF01:2345:6789");
    test(false, ip, 8090);
    test(true, "192.168.1.200", 8080);
    test(false, "1111:1111:1111:1111:1111:1111:111.111.111.111", 1234);
    return 0;
    unsigned char *  buf =NULL;
    int domain, s;
    char str[INET6_ADDRSTRLEN];

    if (argc != 3) {
        fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    domain = (strcmp(argv[1], "i4") == 0) ? AF_INET :
        (strcmp(argv[1], "i6") == 0) ? AF_INET6 : atoi(argv[1]);
    printf("sizof(in6_addr)=%d,  sizeof(struct in_addr)=%d INET6_ADDRSTRLEN=%d, AF_INET=%d, AF_INET6=%d\n",
            sizeof(struct in6_addr), sizeof(struct in_addr),
            INET6_ADDRSTRLEN, AF_INET, AF_INET6);
    if(domain==AF_INET) {
        buf = new unsigned char [sizeof(struct in_addr)];
    } else if(domain == AF_INET6) {
        buf = new unsigned char [sizeof(struct in6_addr)];
    } else {
        fprintf(stderr, "Usage: %s {i4|i6|<num>} string\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    s = inet_pton(domain, argv[2], buf);
    if (s <= 0) {
        if (s == 0)
            fprintf(stderr, "Not in presentation format");
        else
            perror("inet_pton");
        exit(EXIT_FAILURE);
    }

    if (inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL) {
        perror("inet_ntop");
        exit(EXIT_FAILURE);
    }

    printf("%s\n", str);

    exit(EXIT_SUCCESS);
}
